#include "MBTriangleSightFrustum.h"
#include "MBDataTypes.h"

#include "OptTriTri.h"

const MBuint P0 = 0u;
const MBuint P1 = 1u;
const MBuint P2 = 2u;

const MBuint X = 0u;
const MBuint Y = 1u;
const MBuint Z = 2u;

const MBuint NUM_PLANES = 4u;

/**
  * Constructs a new MBTriangleSightFrustum with the given parameters
  *
  * @param trianglePoints const pointer to an array with three elements containing the points of a triangle
  * @param cameraPosition position of the camera in 3D space
  */
MBTriangleSightFrustum::MBTriangleSightFrustum( const MBVec3 *trianglePoints, const MBVec3 &cameraPosition ) :
	mP0( trianglePoints[P0] ),
	mP1( trianglePoints[P1] ),
	mP2( trianglePoints[P2] ),
	mCamPos( cameraPosition )
{
	MBVec3 normal = MBVec3::Cross( trianglePoints[P1] - cameraPosition, trianglePoints[P0] - cameraPosition );
	normal.Normalize();
	mPlanes[P1_P0_CAMPOS].SetNormalAndPoint( normal, cameraPosition );

	normal = MBVec3::Cross( trianglePoints[P2] - cameraPosition, trianglePoints[P1] - cameraPosition );
	normal.Normalize();
	mPlanes[P2_P1_CAMPOS].SetNormalAndPoint( normal, cameraPosition );

	normal = MBVec3::Cross( trianglePoints[P0] - cameraPosition, trianglePoints[P2] - cameraPosition );
	normal.Normalize();
	mPlanes[P0_P2_CAMPOS].SetNormalAndPoint( normal, cameraPosition );

	normal = MBVec3::Cross( trianglePoints[P0] - trianglePoints[P2], trianglePoints[P1] - trianglePoints[P2] );
	normal.Normalize();
	mPlanes[P0_P1_P2].SetNormalAndPoint( normal, trianglePoints[P2] );
}

/**
  * Returns true if this MBTriangleSightFrustum intersects with the axis
  * aligned bounding box defined by the parameters, false otherwise
  *
  * @param boxMin minimum point of the axis aligned bounding box
  * @param boxMax maximum point of the axis aligned bounding box
  * @return true if this MBTriangleSightFrustum intersects with the axis aligned bounding box defined by the parameters, false otherwise
  */
bool MBTriangleSightFrustum::IsIntersectingWithAxisAlignedBox( const MBVec3 &boxMin, const MBVec3 &boxMax ) const
{
	for (MBuint i = 0; i < NUM_PLANES; i++)
	{
		// is the positive vertex outside?
		// (this implies that the box is outside the whole frustum)
		if (mPlanes[i].IsPositiveVertexOfAxisAlignedBoxOutside( boxMin, boxMax ))
		{
			return false;
		}
	}

	return true;
}

/**
  * Returns true if this MBTriangleSightFrustum intersects with the triangle
  * defined by the parameter, false otherwise
  *
  * @param trianglePoints const pointer to an array with three elements containing the points of a triangle
  * @return true if this MBTriangleSightFrustum intersects with the triangle defined by the parameter, false otherwise
  */
bool MBTriangleSightFrustum::IsIntersectingWithTriangle( const MBVec3 *trianglePoints ) const
{
	// 	P1_P0_CAMPOS
	if (NoDivTriTriIsect( &mP1[X], &mP0[X], &mCamPos[X],
						  &trianglePoints[0][X], &trianglePoints[1][X], &trianglePoints[2][X] ))
	{
		return true;
	}
	// 	P2_P1_CAMPOS
	if (NoDivTriTriIsect( &mP2[X], &mP1[X], &mCamPos[X],
						  &trianglePoints[0][X], &trianglePoints[1][X], &trianglePoints[2][X] ))
	{
		return true;
	}
	// 	P0_P2_CAMPOS
	if (NoDivTriTriIsect( &mP0[X], &mP2[X], &mCamPos[X],
						  &trianglePoints[0][X], &trianglePoints[1][X], &trianglePoints[2][X] ))
	{
		return true;
	}
	// 	P0_P1_P2
	if (NoDivTriTriIsect( &mP0[X], &mP1[X], &mP2[X],
						  &trianglePoints[0][X], &trianglePoints[1][X], &trianglePoints[2][X] ))
	{
		return true;
	}

	return false;
}

/**
  * Returns true if the given point is inside this MBTriangleSightFrustum, false
  * otherwise
  *
  * @param point point in 3D space
  * @return true if the given point is inside this MBTriangleSightFrustum, false otherwise
  */
bool MBTriangleSightFrustum::IsPointInside( const MBVec3 &point ) const
{
	for (MBuint i = 0; i < NUM_PLANES; i++)
	{
		// is the point outside of any plane?
		// (this implies that the point is outside the whole frustum)
		if (mPlanes[i].GetDistanceToPoint( point ) < 0.0)
		{
			return false;
		}
	}

	return true;
}